package worker

import (
	"context"
	"fmt"
	"study-crontab/crontab/common"
	"time"

	"go.etcd.io/etcd/api/v3/mvccpb"
	clientv3 "go.etcd.io/etcd/client/v3"
)

type JobMgr struct {
	client  *clientv3.Client
	kv      clientv3.KV
	lease   clientv3.Lease
	watcher clientv3.Watcher
}

//监听任务任务变化
func (m *JobMgr) watchJobs() (err error) {
	var (
		job      *common.Job
		jobEvent *common.JobEvent
	)
	//1.get /cron/jobs/目录下的所有任务, 并且获知当前集群的revision
	jobKey := common.JOB_SAVE_DIR
	//etcd中目录下所有任务
	getResp, err := m.kv.Get(context.TODO(), jobKey, clientv3.WithPrefix())
	if err != nil {
		return err
	}

	//遍历所有任务
	for _, v := range getResp.Kvs {

		job, err = common.UnpackJob(v.Value)
		if err != nil {
			continue
		}
		jobEvent = common.BuildJobEvent(common.JOB_EVENT_SAVE, job)
		G_scheduler.PushJobEvent(jobEvent)
	}

	//2.从该revision向后监听变化事件
	go func() {
		// 从GET时刻的后续版本开始监听变化
		watchStartRevision := getResp.Header.Revision + 1
		// 监听/cron/jobs/目录的后续变化
		watchChan := m.watcher.Watch(context.TODO(), common.JOB_SAVE_DIR, clientv3.WithRev(watchStartRevision), clientv3.WithPrefix())
		//处理监听事件
		for watchResp := range watchChan {
			for _, watchEvent := range watchResp.Events {
				switch watchEvent.Type {
				case mvccpb.PUT: // 任务保存事件
					if job, err = common.UnpackJob(watchEvent.Kv.Value); err != nil {
						continue
					}
					// 构建一个更新Event
					jobEvent = common.BuildJobEvent(common.JOB_EVENT_SAVE, job)
				case mvccpb.DELETE: // 任务被删除了
					// Delete /cron/jobs/job10
					jobName := common.ExtractJobName(string(watchEvent.Kv.Key))
					job = &common.Job{Name: jobName}

					// 构建一个删除Event
					jobEvent = common.BuildJobEvent(common.JOB_EVENT_DELETE, job)
				}
				// 变化推给scheduler
				G_scheduler.PushJobEvent(jobEvent)
			}
		}
	}()
	return
}

var G_jobMgr *JobMgr

// NewJobMgr 初始化管理器
func NewJobMgr() error {
	config := clientv3.Config{
		Endpoints:   G_config.EtcdEndpoints,
		DialTimeout: time.Duration(G_config.EtcdDialTimeout) * time.Millisecond,
	}

	//建立连接
	client, err := clientv3.New(config)
	if err != nil {
		fmt.Println("etcd连接失败")
		return err
	}

	kv := clientv3.NewKV(client)
	lease := clientv3.NewLease(client)
	watcher := clientv3.NewWatcher(client)

	G_jobMgr = &JobMgr{
		client:  client,
		kv:      kv,
		lease:   lease,
		watcher: watcher,
	}

	//启动任务监听
	G_jobMgr.watchJobs()

	//启动监听killer
	G_jobMgr.watchKiller()
	return nil
}

//创建任务执行锁
func (m *JobMgr) CreateJobLock(jobName string) (jobLock *JobLock) {
	jobLock = NewJobLock(m.kv, m.lease, jobName)
	return
}

//监听强杀任务通知
func (m *JobMgr) watchKiller() (err error) {
	var (
		job      *common.Job
		jobEvent *common.JobEvent
	)

	//监听/cron/killer目录
	go func() {
		// 监听/cron/killer/目录的变化
		watchChan := m.watcher.Watch(context.TODO(), common.JOB_KILLER_DIR, clientv3.WithPrefix())
		//处理监听事件
		for watchResp := range watchChan {
			for _, watchEvent := range watchResp.Events {
				switch watchEvent.Type {
				case mvccpb.PUT: // 杀死任务事件
					killerName := common.ExtractKillerName(string(watchEvent.Kv.Key))
					job = &common.Job{Name: killerName}

					// 构建一个杀死任务Event
					jobEvent = common.BuildJobEvent(common.JOB_EVENT_KILLER, job)

				case mvccpb.DELETE: // killer标记过期 被自动删除
				}
				// 变化推给scheduler
				G_scheduler.PushJobEvent(jobEvent)
			}
		}
	}()
	return
}
